package com.shanzmoo.platform.tomerchant.api.manager;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.shanzmoo.base.bean.BaseRedisKeyUtils;
import com.shanzmoo.base.bean.PageReqBase;
import com.shanzmoo.base.bean.PageRespBase;
import com.shanzmoo.base.bean.SHttpResponse;
import com.shanzmoo.base.util.CollectionUtil;
import com.shanzmoo.base.util.JYFException;
import com.shanzmoo.base.util.RedisUtil;
import com.shanzmoo.core.manager.base.AliSmsManager;
import com.shanzmoo.core.util.OcrUtil;
import com.shanzmoo.core.util.RedisCoreKeyUtils;
import com.shanzmoo.db.module.base.entity.SAreaEntity;
import com.shanzmoo.db.module.base.service.ISAreaService;
import com.shanzmoo.db.module.merchant.domain.base.BaseInfoDomain;
import com.shanzmoo.db.module.merchant.domain.dto.*;
import com.shanzmoo.db.module.merchant.domain.query.*;
import com.shanzmoo.db.module.merchant.domain.vo.*;
import com.shanzmoo.db.module.merchant.domain.vo.base.MerchantBaseInfoVo;
import com.shanzmoo.db.module.merchant.domain.vo.base.MerchantPayInfoVo;
import com.shanzmoo.db.module.merchant.domain.vo.base.MerchantSettlementInfoVo;
import com.shanzmoo.db.module.merchant.entity.*;
import com.shanzmoo.db.module.merchant.service.*;
import com.shanzmoo.db.module.pay.entity.SBankEntity;
import com.shanzmoo.db.module.pay.entity.SBusinessEntity;
import com.shanzmoo.db.module.pay.service.ISBankService;
import com.shanzmoo.db.module.pay.service.ISBusinessService;
import com.shanzmoo.pay.domain.request.merchantRequest.SaobeiMerchantAddRequest;
import com.shanzmoo.pay.domain.request.merchantRequest.SaobeiMerchantCheckRequest;
import com.shanzmoo.pay.domain.request.storeRequest.SaobeiStoreAddRequest;
import com.shanzmoo.pay.domain.request.terminalRequest.SaobeiTerminalAddRequest;
import com.shanzmoo.pay.domain.request.terminalRequest.SaobeiTerminalQueryRequest;
import com.shanzmoo.pay.domain.response.merchantResponse.SaobeiMerchantAddResponse;
import com.shanzmoo.pay.domain.response.merchantResponse.SaobeiMerchantCheckResponse;
import com.shanzmoo.pay.domain.response.storeResponse.SaobeiStoreAddResponse;
import com.shanzmoo.pay.domain.response.terminalResponse.SaobeiTerminalAddResponse;
import com.shanzmoo.pay.domain.response.terminalResponse.SaobeiTerminalQueryResponse;
import com.shanzmoo.pay.manager.SaobeiMerchantManager;
import com.shanzmoo.pay.manager.SaobeiStoreManager;
import com.shanzmoo.pay.manager.SaobeiTerminalManager;
import com.shanzmoo.pay.util.ApiConstants;
import com.shanzmoo.pay.util.RandomUtils;
import com.shanzmoo.platform.tomerchant.api.domain.AuthUser;
import com.shanzmoo.platform.tomerchant.api.util.RedisKeyUtils;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

@Slf4j
@Component
@RequiredArgsConstructor
public class MerchantInfoManage {
    // 入驻商户操作服务
    private final IMerchantInfoService iMerchantInfoService;
    // 行业类目操作服务
    private final ISBusinessService isBusinessService;
    // 城市列表操作服务
    private final ISAreaService isAreaService;
    // 银行列表服务信息
    private final ISBankService isBankService;
    // 短信发送服务
    private final AliSmsManager aliSmsManager;
    // 商户门店操作信息
    private final IMerchantShopService iMerchantShopService;
    // 商户台卡操作信息
    private final IMerchantTerminalService iMerchantTerminalService;
    // 商户变更单操作信息
    private final IMerchantChangeInfoService iMerchantChangeInfoService;
    // 商户基本信息操作信息
    private final IMerchantBaseInfoService iMerchantBaseInfoService;
    // 商户结算信息变更操作信息
    private final IMerchantBillingInfoService iMerchantBillingInfoService;
    // 商户操作日志操作信息
    private final IMerchantOperLogService iMerchantOperLogService;
    // 扫呗商户接口管理
    private final SaobeiMerchantManager saobeiMerchantManager;
    // 扫呗门店接口管理
    private final SaobeiStoreManager saobeiStoreManager;
    // 扫呗终端（台卡）接口管理
    private final SaobeiTerminalManager saobeiTerminalManager;

    public UserDetails findByUserName(String username) {
        MerchantInfoEntity user = iMerchantInfoService.lambdaQuery().eq(MerchantInfoEntity::getCode, username).one();
        if (user == null) {
            throw new UsernameNotFoundException("用户不存在");
        }
        return AuthUser.create(user);
    }

    /**
     * 保存入驻商户
     *
     * @param merchantInfo
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public SHttpResponse<Integer> saveMerchantInfo(MerchantInfoDto merchantInfo) throws JYFException {
        // 1、验证参数信息并组装数据入库参数
        MerchantInfoEntity merchantInfoEntity = checkMerchantInfoAndCreateEntity(merchantInfo);
        // 2、保存入库并返回
        boolean save = iMerchantInfoService.save(merchantInfoEntity);
        return SHttpResponse.ok(merchantInfoEntity.getId());
    }

    /**
     * 根据查询条件查询入驻商户明细信息
     *
     * @param query
     * @return
     */
    public SHttpResponse<MerchantInfoVo> selectMerchantInfo(MerchantInfoQuery query) {
        // 1、查询数据库数据信息
        MerchantInfoEntity merchantInfoEntity = iMerchantInfoService.lambdaQuery()
                .eq(StringUtils.isNotBlank(query.getCode()), MerchantInfoEntity::getCode, query.getCode())
                .eq(StringUtils.isNotBlank(query.getMobile()), MerchantInfoEntity::getMobile, query.getMobile())
                .one();
        // 2、组装返回值信息
        MerchantInfoVo merchantInfo = new MerchantInfoVo();
        MerchantBaseInfoVo baseInfoVo = new MerchantBaseInfoVo();
        BeanUtils.copyProperties(merchantInfoEntity, baseInfoVo);
        MerchantSettlementInfoVo settlementInfoVo = new MerchantSettlementInfoVo();
        BeanUtils.copyProperties(merchantInfoEntity, settlementInfoVo);
        MerchantPayInfoVo payInfoVo = new MerchantPayInfoVo();
        BeanUtils.copyProperties(merchantInfoEntity, payInfoVo);
        merchantInfo.setMerchanrBaseInfo(baseInfoVo);
        merchantInfo.setMerchantSettlementInfo(settlementInfoVo);
        merchantInfo.setMerchantPayInfoVo(payInfoVo);
        return SHttpResponse.ok(merchantInfo);
    }

    /**
     * 获取行业类别信息
     *
     * @return
     */
    public SHttpResponse<List<BusinessVo>> selectBusinessInfo(BusinessQuery query) {
        // 1、获取当前制定条件的所有行业类目信息
        List<SBusinessEntity> businessEntities = isBusinessService.lambdaQuery()
                .eq(SBusinessEntity::getDeleted, 0)
                .eq(query.getBusinessLevel() != null, SBusinessEntity::getBusinessLevel, query.getBusinessLevel())
                .eq(StringUtils.isNotBlank(query.getBusinessParentCode()), SBusinessEntity::getBusinessParentCode, query.getBusinessParentCode())
                .list();
        // 2、组装返回值信息
        List<BusinessVo> businessVos = new ArrayList<>();
        businessEntities.stream().forEach(item -> {
            BusinessVo businessVo = new BusinessVo();
            BeanUtils.copyProperties(item, businessVo);
            businessVos.add(businessVo);
        });
        return SHttpResponse.ok(businessVos);
    }

    /**
     * 获取地区信息
     *
     * @return
     */
    public SHttpResponse<List<AreaVo>> selectAreaInfo(@RequestBody AreaQuery query) {
        // 1、获取当前制定条件的所有地区信息
        List<SAreaEntity> areaEntities = isAreaService.lambdaQuery()
                .eq(SAreaEntity::getDeleted, 0)
                .eq(StringUtils.isNotBlank(query.getAreaCode()), SAreaEntity::getAreaCode, query.getAreaCode())
                .eq(StringUtils.isNotBlank(query.getAreaParentCode()), SAreaEntity::getAreaParentCode, query.getAreaParentCode())
                .like(StringUtils.isNotBlank(query.getAreaName()), SAreaEntity::getAreaName, query.getAreaName())
                .eq(StringUtils.isNotBlank(query.getAreaLevel()), SAreaEntity::getAreaLevel, query.getAreaLevel())
                .orderByDesc(query.getOrderNum() != null, SAreaEntity::getOrderNum)
                .list();
        // 2、组装返回值信息
        List<AreaVo> areaVos = new ArrayList<>();
        areaEntities.stream().forEach(item -> {
            AreaVo areaVo = new AreaVo();
            BeanUtils.copyProperties(item, areaVo);
            areaVos.add(areaVo);
        });
        return SHttpResponse.ok(areaVos);
    }

    /**
     * 通过OCR识别获取图片识别信息
     *
     * @return
     */
    public SHttpResponse<MerchantOCRDistinguishVo> selectOCRDistinguish(@RequestBody MerchantOCRDistinguishQuery query) {
        try {
            // 1、获取文件路径信息
            byte[] image = getURLImage(query.getImageUrl());
            // 2、根据识别类型分别做不同识别
            OcrUtil.OcrReturn ocrReturn = null;
            switch (query.getDistinguishType()) {
                case 0:
                    // 营业执照
                    ocrReturn = OcrUtil.businessLicenseDistinguish(image);
                    break;
                case 1:
                    // 身份证
                    ocrReturn = OcrUtil.idCardDistinguish(image);
                    break;
                case 2:
                    // 银行卡
                    ocrReturn = OcrUtil.bankCardDistinguish(image);
                    break;
                default:
                    log.error("识别类型错误，入参为：{}", JSONObject.toJSONString(query));
                    return SHttpResponse.fail("识别类型错误");
            }
            // 3、组装返回数据
            if (!ocrReturn.getFlag()) {
                log.error(ocrReturn.getError());
                return SHttpResponse.fail(ocrReturn.getError());
            }
            MerchantOCRDistinguishVo merchantOCRDistinguishVo = initMerchantOCRDistinguish(query.getDistinguishType(), ocrReturn);
            return SHttpResponse.ok(merchantOCRDistinguishVo);
        } catch (RuntimeException e) {
            log.error(e.getMessage());
            return SHttpResponse.fail(e.getMessage());
        } catch (Exception e) {
            log.error("识别文件解析错误");
            return SHttpResponse.fail("识别文件解析错误");
        }
    }

    /**
     * 通过银卡编码获取支行信息等
     *
     * @return
     */
    public SHttpResponse<List<BankVo>> selectBank(@RequestBody BankQuery query) {
        // 1、获取当前制定条件的所有银行信息
        List<SBankEntity> bankEntities = isBankService.lambdaQuery()
                .eq(SBankEntity::getDeleted, 0)
                .eq(StringUtils.isNotBlank(query.getBankCode()), SBankEntity::getBankCode, query.getBankCode())
                .eq(StringUtils.isNotBlank(query.getBankName()), SBankEntity::getBankName, query.getBankName())
                .eq(StringUtils.isNotBlank(query.getBankParentCode()), SBankEntity::getBankParentCode, query.getBankParentCode())
                .eq(StringUtils.isNotBlank(query.getAreaCode()), SBankEntity::getAreaCode, query.getAreaCode())
                .eq(query.getBankLevel() != null, SBankEntity::getBankLevel, query.getBankLevel())
                .list();
        // 2、获取父级数据信息
        List<BankVo> bankVos = new ArrayList<>();
        if (query.getBankLevel() != null && query.getBankLevel() == 2){
            SBankEntity bankEntity = isBankService.lambdaQuery()
                    .eq(SBankEntity::getBankCode, query.getBankParentCode())
                    .one();
            // 2、组装返回值信息
            bankEntities.stream().forEach(item -> {
                BankVo bankVo = new BankVo();
                BeanUtils.copyProperties(item, bankVo);
                if (query.getBankLevel() == 2 && bankEntity != null){
                    String subStr = bankEntity.getBankName() + "股份有限公司";
                    if (bankVo.getBankName().contains(subStr)){
                        bankVo.setBankName(bankVo.getBankName().substring(subStr.length()));
                    } else if (bankVo.getBankName().contains(bankEntity.getBankName())){
                        bankVo.setBankName(bankVo.getBankName().substring(bankEntity.getBankName().length()));
                    }
                }
                bankVos.add(bankVo);
            });
        } else {
            bankEntities.stream().forEach(item -> {
                BankVo bankVo = new BankVo();
                BeanUtils.copyProperties(item, bankVo);
                bankVos.add(bankVo);
            });
        }
        return SHttpResponse.ok(bankVos);
    }

    /**
     * 发送短信信息
     *
     * @return
     */
    public SHttpResponse<Boolean> sendSmsMsg(@RequestBody MerchantSmsInfoDto smsInfoVo) {
        // 1、构建验证码信息并存入缓存
        String code = MerchantInfoManage.createRandom(true, 6);
        // 2、存入缓存(10分钟有效)
        RedisUtil.set(RedisKeyUtils.installMerchantCheckKey(smsInfoVo.getCode()), code, 10l);
        // 3、发送验证短信
        aliSmsManager.sendLoginSms(smsInfoVo.getOldMobilePhone(), code);
        return SHttpResponse.ok();
    }

    /**
     * 验证短信信息
     *
     * @return
     */
    public SHttpResponse<Boolean> checkSmsMsg(@RequestBody MerchantSmsInfoDto smsInfoVo) {
        // 1、获取缓存内验证码信息
        String code = RedisUtil.get(RedisKeyUtils.installMerchantCheckKey(smsInfoVo.getCode()));
        // 2、验证验证码
        if (StringUtils.isNotBlank(code) && code.toLowerCase().equals(smsInfoVo.getVerifyCode().toLowerCase())){
            // 验证成功，则返回正常并删除缓存
            RedisUtil.del(RedisKeyUtils.installMerchantCheckKey(smsInfoVo.getCode()));
            return SHttpResponse.ok(true);
        }
        // 3、验证失败直接返回
        return SHttpResponse.fail(100001,"验证码无效");
    }

    /**
     * 修改电话号码
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public SHttpResponse<Boolean> updateMerchantMobilePhone(@RequestBody MerchantSmsInfoDto smsInfoVo) throws JYFException {
        // 1、获取商户原基本信息
        MerchantInfoEntity merchantInfoEntity = iMerchantInfoService.lambdaQuery()
                .eq(MerchantInfoEntity::getCode, smsInfoVo.getCode())
                .one();
        if (merchantInfoEntity == null) {
            log.error("获取商户原基本信息异常，查询条件为：{}", JSONObject.toJSONString(smsInfoVo));
            throw new JYFException("获取商户原基本信息为空");
        }
        // 2、原基本信息存入基本信息备份表
        MerchantBaseInfoEntity merchantBaseInfoEntity = new MerchantBaseInfoEntity();
        initMerchantManageBaseInfoEntity(merchantBaseInfoEntity, merchantInfoEntity);
        // 3、当前新数据入库
        merchantBaseInfoEntity.setMobile(smsInfoVo.getNewMobilePhone());
        merchantBaseInfoEntity.setId(null);
        iMerchantBaseInfoService.save(merchantBaseInfoEntity);
        // 4、写变更单（基本信息）
        MerchantChangeInfoEntity merchantChangeInfoEntity = initMerchantChangeInfo(smsInfoVo, "修改电话号码信息", 0, 0);
        boolean save = iMerchantChangeInfoService.save(merchantChangeInfoEntity);
        // 5、写操作日志
        MerchantOperLogEntity merchantOperLogEntity = initMerchantOperLog(smsInfoVo, "修改电话号码信息", 4);
        iMerchantOperLogService.save(merchantOperLogEntity);
        return SHttpResponse.ok();
    }

    /**
     * 修改邮箱信息
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public SHttpResponse<Boolean> updateMerchantEmail(@RequestBody MerchantEmailInfoDto emailInfoDto) throws JYFException {
        // 1、获取商户原基本信息
        MerchantInfoEntity merchantInfoEntity = iMerchantInfoService.lambdaQuery()
                .eq(MerchantInfoEntity::getCode, emailInfoDto.getCode())
                .one();
        if (merchantInfoEntity == null) {
            log.error("获取商户原基本信息异常，查询条件为：{}", JSONObject.toJSONString(emailInfoDto));
            throw new JYFException("获取商户原基本信息为空");
        }
        // 2、原基本信息存入基本信息备份表
        MerchantBaseInfoEntity merchantBaseInfoEntity = new MerchantBaseInfoEntity();
        initMerchantManageBaseInfoEntity(merchantBaseInfoEntity, merchantInfoEntity);
        // 3、当前新数据入库
        merchantBaseInfoEntity.setContactsEmaill(emailInfoDto.getNewEmail());
        merchantBaseInfoEntity.setId(null);
        iMerchantBaseInfoService.save(merchantBaseInfoEntity);
        // 2、写变更单（基本信息）
        MerchantChangeInfoEntity merchantChangeInfoEntity = initMerchantChangeInfo(emailInfoDto, "修改邮箱信息", 0, 0);
        boolean save = iMerchantChangeInfoService.save(merchantChangeInfoEntity);
        // 2、写操作日志
        MerchantOperLogEntity merchantOperLogEntity = initMerchantOperLog(emailInfoDto, "修改邮箱信息", 3);
        iMerchantOperLogService.save(merchantOperLogEntity);
        return SHttpResponse.ok();
    }

    /**
     * 修改商户地址
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public SHttpResponse<Boolean> updateMerchantAddressInfo(@RequestBody MerchantAddressInfoDto addressInfo) throws JYFException {
        // 1、获取商户原基本信息
        MerchantInfoEntity merchantInfoEntity = iMerchantInfoService.lambdaQuery()
                .eq(MerchantInfoEntity::getCode, addressInfo.getCode())
                .one();
        if (merchantInfoEntity == null) {
            log.error("获取商户原基本信息异常，查询条件为：{}", JSONObject.toJSONString(addressInfo));
            throw new JYFException("获取商户原基本信息为空");
        }
        // 2、原基本信息存入基本信息备份表
        MerchantBaseInfoEntity merchantBaseInfoEntity = new MerchantBaseInfoEntity();
        initMerchantManageBaseInfoEntity(merchantBaseInfoEntity, merchantInfoEntity);
        // 3、当前新数据入库
        merchantBaseInfoEntity.setAddressCode(addressInfo.getAddressCode());
        merchantBaseInfoEntity.setAddressName(addressInfo.getAddressName());
        merchantBaseInfoEntity.setDetailedAddress(addressInfo.getDetailedAddress());
        merchantBaseInfoEntity.setId(null);
        iMerchantBaseInfoService.save(merchantBaseInfoEntity);
        // 4、写变更单（基本信息）
        MerchantChangeInfoEntity merchantChangeInfoEntity = initMerchantChangeInfo(addressInfo, "修改商户地址信息", 0, 0);
        boolean save = iMerchantChangeInfoService.save(merchantChangeInfoEntity);
        // 5、写操作日志
        MerchantOperLogEntity merchantOperLogEntity = initMerchantOperLog(addressInfo, "修改商户地址", 1);
        iMerchantOperLogService.save(merchantOperLogEntity);
        return SHttpResponse.ok();
    }

    /**
     * 修改结算信息
     *
     * @return
     */
    public SHttpResponse<Boolean> updateMerchantSettlementInfo(@RequestBody MerchantSettlementInfoDto settlementInfo) throws JYFException {
        // 1、查询当前商户是否存在待审核信息，若存在，则不允许再次修改
        List<MerchantChangeInfoEntity> list = iMerchantChangeInfoService.lambdaQuery()
                .eq(MerchantChangeInfoEntity::getCode, settlementInfo.getCode())
                .ne(MerchantChangeInfoEntity::getChangeStatus, 3)
                .list();
        if (CollectionUtil.isNotEmpty(list)){
            log.error("当前存在正在审核的商户结算信息，不能再次更改商户信息");
            return SHttpResponse.fail(100004, "当前存在正在审核的商户结算信息，不能再次更改商户信息");
        }
        // 2、获取商户原基本信息
        MerchantInfoEntity merchantInfoEntity = iMerchantInfoService.lambdaQuery()
                .eq(MerchantInfoEntity::getCode, settlementInfo.getCode())
                .one();
        if (merchantInfoEntity == null) {
            log.error("获取商户原基本信息异常，查询条件为：{}", JSONObject.toJSONString(settlementInfo));
            throw new JYFException("获取商户原基本信息为空");
        }
        // 2、原基本信息存入基本信息备份表商户结算信息存入商户结算备份表
        MerchantBillingInfoEntity merchantBillingInfoEntity = new MerchantBillingInfoEntity();
        initMerchantBillingInfoEntity(merchantBillingInfoEntity, merchantInfoEntity);
        BeanUtils.copyProperties(settlementInfo, merchantBillingInfoEntity);
        merchantBillingInfoEntity.setMerchantId(settlementInfo.getCode());
        merchantBillingInfoEntity.setId(null);
        iMerchantBillingInfoService.save(merchantBillingInfoEntity);
        // 3、写商户变更表
        // 3.1、初始化商户变更记录信息实体
        MerchantChangeInfoEntity merchantChangeInfoEntity = initMerchantChangeSettlementInfo(settlementInfo);
        merchantChangeInfoEntity.setChangeLinkId(merchantBillingInfoEntity.getId());
        boolean save = iMerchantChangeInfoService.save(merchantChangeInfoEntity);
        // 4、写操作日志记录表
        MerchantOperLogEntity merchantOperLogEntity = initMerchantOperLog(settlementInfo, "修改结算信息", 2);
        iMerchantOperLogService.save(merchantOperLogEntity);
        return SHttpResponse.ok(true);
    }

    /**
     * 保存/修改门店信息
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public SHttpResponse<Boolean> saveMerchantShopEntity(@RequestBody MerchantShopVo merchantShop) throws JYFException {
        // 1、初始化门店信息
        MerchantShopEntity merchantShopEntity = new MerchantShopEntity();
        BeanUtils.copyProperties(merchantShop, merchantShopEntity);
        merchantShopEntity.setCode(merchantShop.getMerchantCode().substring(0, merchantShop.getMerchantCode().length() > 18 ? 18 : merchantShop.getMerchantCode().length()));
        // 2、保存/修改门店信息
        boolean result = iMerchantShopService.saveOrUpdate(merchantShopEntity);
        // 3、推送保存或修改门店信息到扫呗
        SaobeiStoreAddRequest addRequest = initSaobeiStoreAddRequest(merchantShopEntity);
        SaobeiStoreAddResponse addResponse = saobeiStoreManager.addStore(addRequest);
        if (!"01".equals(addResponse.getReturn_code()) || ("01".equals(addResponse.getReturn_code()) && !"01".equals(addResponse.getResult_code()))){
            log.error("扫呗创建门店异常，检查返回结果信息为：{}", JSONObject.toJSONString(addResponse));
            throw new JYFException(addResponse.getReturn_msg());
        } else {
            log.info("扫呗创建门店成功");
            // 成功，则更新门店编码
            boolean update = iMerchantShopService.lambdaUpdate()
                    .eq(MerchantShopEntity::getId, merchantShopEntity.getId())
                    .set(MerchantShopEntity::getCode, addResponse.getStore_code())
                    .update();
        }
        // 4、写操作日志
        BaseInfoDomain baseInfoDomain = new BaseInfoDomain();
        baseInfoDomain.setCode(merchantShop.getMerchantCode());
        MerchantOperLogEntity merchantOperLogEntity = initMerchantOperLog(baseInfoDomain, "创建门店信息", 10);
        iMerchantOperLogService.save(merchantOperLogEntity);
        return SHttpResponse.ok(result);
    }

    /**
     * 分页查询门店信息
     *
     * @return
     */
    public SHttpResponse<PageRespBase<MerchantShopVo>> selectMerchantShop(@RequestBody MerchantShopQuery query) {
        Page<MerchantShopEntity> page = iMerchantShopService.lambdaQuery()
                .eq(query.getId() != null, MerchantShopEntity::getId, query.getCode())
                .eq(StringUtils.isNotBlank(query.getMerchantCode()), MerchantShopEntity::getMerchantCode, query.getMerchantCode())
                .eq(StringUtils.isNotBlank(query.getCode()), MerchantShopEntity::getCode, query.getCode())
                .like(StringUtils.isNotBlank(query.getName()), MerchantShopEntity::getName, query.getName())
                .between(query.getStartTime() != null, MerchantShopEntity::getCreateTime, query.getStartTime(), query.getEndTime())
                .orderByDesc(MerchantShopEntity::getCreateTime)
                .page(new Page<>(query.getPageNum(), query.getPageSize()));
        // 3、组装数据信息
        PageRespBase<MerchantShopVo> respBase = new PageRespBase<MerchantShopVo>();
        respBase.setCurrPage(query.getPageNum());
        respBase.setPageSize(query.getPageSize());
        respBase.setTotalCount(page.getTotal());
        respBase.setTotalPage(page.getTotal() % page.getSize() == 0 ? page.getTotal() / page.getSize() : page.getTotal() / page.getSize() + 1);
        List<MerchantShopVo> merchantShopVos = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(page.getRecords())){
            page.getRecords().forEach(item -> {
                MerchantShopVo merchantShopVo = new MerchantShopVo();
                BeanUtils.copyProperties(item, merchantShopVo);
                merchantShopVos.add(merchantShopVo);
            });
        }
        respBase.setList(merchantShopVos);
        return SHttpResponse.ok(respBase);
    }

    /**
     * 查询门店详情信息
     *
     * @return
     */
    public SHttpResponse<MerchantShopVo> selectMerchantShopDetail(@RequestParam(value = "id") Integer id) {
        // 1、通过ID查询门店信息
        MerchantShopEntity merchantShopEntity = iMerchantShopService.getById(id);
        // 2、组装返回数据
        if (merchantShopEntity != null){
            MerchantShopVo merchantShopVo = new MerchantShopVo();
            BeanUtils.copyProperties(merchantShopEntity, merchantShopVo);
            return SHttpResponse.ok(merchantShopVo);
        }
        return SHttpResponse.fail(100002, "门店信息不存在");
    }


    /**
     * 保存/修改商户台卡信息
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public SHttpResponse<Boolean> saveMerchantTerminal(@RequestBody MerchantTerminalVo merchantTerminal) throws JYFException {
        // 1、初始化商户台卡信息
        MerchantTerminalEntity merchantTerminalEntity = new MerchantTerminalEntity();
        BeanUtils.copyProperties(merchantTerminal, merchantTerminalEntity);
        // 2、保存/修改商户台卡信息
        boolean result = iMerchantTerminalService.saveOrUpdate(merchantTerminalEntity);
        // 3、推送保存或修改台卡信息到扫呗
        SaobeiTerminalAddRequest addRequest = initSaobeiTerminalAddRequest(merchantTerminal);
        SaobeiTerminalAddResponse addResponse = saobeiTerminalManager.addTerminal(addRequest);
        if (!"01".equals(addResponse.getReturn_code())  || ("01".equals(addResponse.getReturn_code()) && !"01".equals(addResponse.getResult_code()))){
            log.error("扫呗创建商户台卡异常，检查返回结果信息为：{}", JSONObject.toJSONString(addResponse));
            throw new JYFException(addResponse.getReturn_msg());
        } else {
            log.info("扫呗创建商户台卡成功，返回信息为：{}", JSONObject.toJSONString(addResponse));
            // 更新当前台卡信息
            boolean update = iMerchantTerminalService.lambdaUpdate()
                    .eq(MerchantTerminalEntity::getId, merchantTerminalEntity.getId())
                    .set(MerchantTerminalEntity::getTerminalId, addResponse.getTerminal_id())
                    .update();
        }
        // 4、写操作日志
        BaseInfoDomain baseInfoDomain = new BaseInfoDomain();
        baseInfoDomain.setCode(merchantTerminal.getMerchantNo());
        MerchantOperLogEntity merchantOperLogEntity = initMerchantOperLog(baseInfoDomain, "创建商户台卡信息", 11);
        iMerchantOperLogService.save(merchantOperLogEntity);
        return SHttpResponse.ok(result);
    }

    /**
     * 开通或关闭商户台卡信息
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public SHttpResponse<Boolean> updateMerchantTerminal(@RequestBody MerchantTerminalVo merchantTerminal) {
        // 1、更新台卡信息
        boolean update = iMerchantTerminalService.lambdaUpdate()
                .eq(MerchantTerminalEntity::getId, merchantTerminal.getId())
                .set(MerchantTerminalEntity::getDeleted, merchantTerminal.getDeleted())
                .set(MerchantTerminalEntity::getUpdateTime, LocalDateTime.now())
                // 更新修改人
                .set(MerchantTerminalEntity::getUpdateUser, "111")
                .update();
        return update ? SHttpResponse.ok(true) : SHttpResponse.fail(100006, "更新台卡信息失败");
    }

    /**
     * 分页查询商户台卡信息
     *
     * @return
     */
    public SHttpResponse<PageRespBase<MerchantTerminalVo>> selectMerchantTerminal(@RequestBody MerchantTerminalQuery query) {
        // 1、按照条件查询商户台卡信息
        Page<MerchantTerminalEntity> page = iMerchantTerminalService.lambdaQuery()
                .eq(StringUtils.isNotBlank(query.getStoreCode()), MerchantTerminalEntity::getStoreCode, query.getStoreCode())
                .eq(StringUtils.isNotBlank(query.getMerchantNo()), MerchantTerminalEntity::getMerchantNo, query.getMerchantNo())
                .orderByDesc(MerchantTerminalEntity::getCreateTime)
                .page(new Page<>(query.getPageNum(), query.getPageSize()));
        // 2、组装数据信息
        PageRespBase<MerchantTerminalVo> respBase = new PageRespBase<MerchantTerminalVo>();
        respBase.setCurrPage(query.getPageNum());
        respBase.setPageSize(query.getPageSize());
        respBase.setTotalCount(page.getTotal());
        respBase.setTotalPage(page.getTotal() % page.getSize() == 0 ? page.getTotal() / page.getSize() : page.getTotal() / page.getSize() + 1);
        List<MerchantTerminalVo> merchantTerminalVos = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(page.getRecords())){
            page.getRecords().forEach(item -> {
                MerchantTerminalVo merchantTerminalVo = new MerchantTerminalVo();
                BeanUtils.copyProperties(item, merchantTerminalVo);
                merchantTerminalVos.add(merchantTerminalVo);
            });
        }
        respBase.setList(merchantTerminalVos);
        return SHttpResponse.ok(respBase);
    }

    /**
     * 查询商户台卡详情信息
     *
     * @return
     */
    public SHttpResponse<MerchantTerminalVo> selectMerchantTerminalDetail(@RequestParam(value = "id") Integer id) {
        // 1、通过ID查询商户台卡信息
        MerchantTerminalEntity merchantTerminalEntity = iMerchantTerminalService.getById(id);
        // 2、组装返回数据
        if (merchantTerminalEntity != null){
            MerchantTerminalVo merchantTerminalVo = new MerchantTerminalVo();
            BeanUtils.copyProperties(merchantTerminalEntity, merchantTerminalVo);
            return SHttpResponse.ok(merchantTerminalVo);
        }
        return SHttpResponse.fail(100002, "商户台卡信息不存在");
    }


    /********************************************************* private **********************************************************/

    /**
     * 通过数据实体构建变更单信息(保存使用)
     *
     * @param billingInfoEntity
     * @return
     */
    private MerchantBillingInfoEntity initMerchantBillingInfoEntity(MerchantBillingInfoEntity billingInfoEntity, MerchantInfoEntity merchantInfoEntity){
        billingInfoEntity.setBankAddressOld(merchantInfoEntity.getBankAddress());
        billingInfoEntity.setBankAccountCodeOld(merchantInfoEntity.getBankAccountCode());
        billingInfoEntity.setBankAccountNameOld(merchantInfoEntity.getBankAccountName());
        billingInfoEntity.setBankInfoOld(merchantInfoEntity.getBankInfo());
        billingInfoEntity.setBankBranchOld(merchantInfoEntity.getBankBranch());
        billingInfoEntity.setBankCardSecondImgOld(merchantInfoEntity.getBankCardSecondImg());
        billingInfoEntity.setBankCardFirstImgOld(merchantInfoEntity.getBankCardFirstImg());
        billingInfoEntity.setBankPhoneNumOld(merchantInfoEntity.getBankPhoneNum());
        return billingInfoEntity;
    }

    /**
     * 通过数据实体构建变更单信息(保存使用)
     *
     * @param baseInfo
     * @return
     */
    private MerchantBaseInfoEntity initMerchantManageBaseInfoEntity(MerchantBaseInfoEntity baseInfo, MerchantInfoEntity merchantInfoEntity){
        baseInfo.setNameOld(merchantInfoEntity.getName());
        baseInfo.setAbbreviationNameOld(merchantInfoEntity.getAbbreviationName());
        baseInfo.setMobileOld(merchantInfoEntity.getMobile());
        baseInfo.setCategoryCodeOld(merchantInfoEntity.getCategoryCode());
        baseInfo.setCategoryNameOld(merchantInfoEntity.getCategoryName());
        baseInfo.setAddressCodeOld(merchantInfoEntity.getAddressCode());
        baseInfo.setAddressNameOld(merchantInfoEntity.getAddressName());
        baseInfo.setDetailedAddressOld(merchantInfoEntity.getDetailedAddress());
        baseInfo.setContactsNameOld(merchantInfoEntity.getContactsName());
        baseInfo.setContactsEmaillOld(merchantInfoEntity.getContactsEmaill());
        return baseInfo;
    }

    /**
     * 初始化商户台卡信息
     * @param merchantTerminal
     * @return
     */
    private SaobeiTerminalAddRequest initSaobeiTerminalAddRequest(MerchantTerminalVo merchantTerminal){
        // 创建端创建requst
        SaobeiTerminalAddRequest request = new SaobeiTerminalAddRequest();
        request.setInst_no(ApiConstants.INST_NO);
        request.setStore_code(merchantTerminal.getStoreCode());
        request.setMerchant_no(merchantTerminal.getMerchantNo());
        request.setTerminal_name(merchantTerminal.getTerminalName());
        request.setTrace_no(RandomUtils.getRandom(32));
        return request;
    }
    /**
     * 初始化商户门店信息
     * @param merchantShopEntity
     * @return
     */
    private SaobeiStoreAddRequest initSaobeiStoreAddRequest(MerchantShopEntity merchantShopEntity){
        // 创建门店 requst
        SaobeiStoreAddRequest request = new SaobeiStoreAddRequest();
        request.setInst_no(ApiConstants.INST_NO);
        request.setMerchant_no(merchantShopEntity.getMerchantCode());
        request.setStore_name(merchantShopEntity.getName());
        request.setStore_addre(merchantShopEntity.getAddress());
        request.setStore_person(merchantShopEntity.getContacts());
        request.setStore_phone(merchantShopEntity.getMobile());
        request.setStore_email(merchantShopEntity.getEmail());
        request.setTrace_no(RandomUtils.getRandom(32));
        return request;
    }

    /**
     * 根据商户基本信息初始化变更单
     * @param merchantBaseInfoVo
     * @return
     */
    private MerchantChangeInfoEntity initMerchantChangeInfo(MerchantBaseInfoVo merchantBaseInfoVo){
        return initMerchantChangeInfo(merchantBaseInfoVo, "基本信息变更", 0, 3);
    }

    /**
     * 根据结算信息初始化变更单
     * @param settlementInfo
     * @return
     */
    private MerchantChangeInfoEntity initMerchantChangeSettlementInfo(MerchantSettlementInfoDto settlementInfo){
        return initMerchantChangeInfo(settlementInfo, "结算信息变更", 1, 0);
    }

    /**
     * 初始化变更单
     * @param baseInfoDomain
     * @param changeDesc
     * @param changeType
     * @param changeStatus
     * @return
     */
    private MerchantChangeInfoEntity initMerchantChangeInfo(BaseInfoDomain baseInfoDomain, String changeDesc, int changeType, int changeStatus){
        MerchantChangeInfoEntity merchantChangeInfoEntity = new MerchantChangeInfoEntity();
        merchantChangeInfoEntity.setChangeDesc(changeDesc);
        // 结算信息需要审核
        merchantChangeInfoEntity.setChangeStatus(changeStatus);
        merchantChangeInfoEntity.setChangeType(changeType);
        // 当前登录人ID
        merchantChangeInfoEntity.setChangeUser(111);
        merchantChangeInfoEntity.setCode(baseInfoDomain.getCode());
        merchantChangeInfoEntity.setName(baseInfoDomain.getName());
        LocalDateTime now = LocalDateTime.now();
        merchantChangeInfoEntity.setSubmitTime(now);
        merchantChangeInfoEntity.setHandlerTime(now);
        return merchantChangeInfoEntity;
    }

    /**
     * 初始化操作日志信息
     * @param baseInfoDomain
     * @param operDesc 操作描述
     * @param operType 操作类型
     * @return
     */
    private MerchantOperLogEntity initMerchantOperLog(BaseInfoDomain baseInfoDomain, String operDesc, Integer operType){
        MerchantOperLogEntity merchantOperLogEntity = new MerchantOperLogEntity();
        merchantOperLogEntity.setMerchantNo(baseInfoDomain.getCode());
        merchantOperLogEntity.setRemark(operDesc);
        LocalDateTime now = LocalDateTime.now();
        merchantOperLogEntity.setOperTime(now);
        merchantOperLogEntity.setOperType(operType);
        return merchantOperLogEntity;
    }

    /**
     * 验证入参及组装返回值信息
     *
     * @param merchantInfo
     * @return
     */
    private MerchantInfoEntity checkMerchantInfoAndCreateEntity(MerchantInfoDto merchantInfo) throws JYFException {
        // 1、商户类型为“小微”时，结算类型仅可为“法人对私”
        if (merchantInfo.getType() == 2 && merchantInfo.getBillingType() != 1){
            throw new JYFException("“小微”商户仅支持“法人对私”方式结算");
        }
        // 2、如果法人与联系人信息一致，则直接将联系人证照信息赋值给法人信息
        if (merchantInfo != null && merchantInfo.getUniformity() == 0){
            merchantInfo.setLegalPersonName(merchantInfo.getContactsName());
            merchantInfo.setLegalPersonCardCode(merchantInfo.getContactsCardCode());
            merchantInfo.setLegalPersonCardValidity(merchantInfo.getContactsCardValidity());
            merchantInfo.setLegalPersonCardFirstImg(merchantInfo.getContactsCardFirstImg());
            merchantInfo.setLegalPersonCardSecondImg(merchantInfo.getContactsCardSecondImg());
        }
        // 3、对公结算，开户许可证必传
        if (merchantInfo.getBillingType() == 0 && StringUtils.isBlank(merchantInfo.getAccountPermit())){
            throw new JYFException("对公结算，开户许可证必传");
        }
        // 4、非法人对私结算则必须传非法人证明材料
        if (merchantInfo.getBillingType() == 2 && StringUtils.isBlank(merchantInfo.getNoLegalMaterials())){
            throw new JYFException("非法人对私结算则必须传非法人证明材料");
        }
        // 5、商户名称和简称不可重复
        List<MerchantInfoEntity> list = iMerchantInfoService.lambdaQuery()
                .eq(MerchantInfoEntity::getName, merchantInfo.getName())
                .or()
                .eq(MerchantInfoEntity::getAbbreviationName, merchantInfo.getAbbreviationName())
                .list();
        if (CollectionUtil.isNotEmpty(list)){
            throw new JYFException("商户名称和简称均不可重复");
        }
        // 6、验证通过，则返回构造的数据信息
        MerchantInfoEntity merchantInfoEntity = new MerchantInfoEntity();
        BeanUtils.copyProperties(merchantInfo, merchantInfoEntity);
        merchantInfoEntity.setCode(System.currentTimeMillis() + "");
        LocalDateTime now = LocalDateTime.now();
        merchantInfoEntity.setCreateTime(now);
        merchantInfoEntity.setUpdateTime(now);
        return merchantInfoEntity;
    }

    /**
     * 获取网络图片
     *
     * @param imageUrl 图片URL
     * @return
     * @throws Exception
     */
    public static byte[] getURLImage(String imageUrl) throws Exception {
        // new一个URL对象
        URL url = new URL(imageUrl);
        //打开链接
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        //设置请求方式为"GET"
        conn.setRequestMethod("GET");
        //超时响应时间为30秒
        conn.setConnectTimeout(30 * 1000);
        //通过输入流获取图片数据
        InputStream inStream = conn.getInputStream();
        //得到图片的二进制数据，以二进制封装得到数据，具有通用性
        return readInputStream(inStream);
    }

    /**
     * 二进制去封装图片
     *
     * @param inStream 数据流
     * @return
     * @throws Exception
     */
    private static byte[] readInputStream(InputStream inStream) throws Exception {
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        //创建一个Buffer字符串
        byte[] buffer = new byte[1024];
        //每次读取的字符串长度，如果为-1，代表全部读取完毕
        int len = 0;
        //使用一个输入流从buffer里把数据读取出来
        while ((len = inStream.read(buffer)) != -1) {
            //用输出流往buffer里写入数据，中间参数代表从哪个位置开始读，len代表读取的长度
            outStream.write(buffer, 0, len);
        }
        //关闭输入流
        inStream.close();
        //把outStream里的数据写入内存
        return outStream.toByteArray();
    }

    /**
     * 组装OCR正常识别后数据信息
     *
     * @param type
     * @param ocrReturn
     * @return
     */
    private MerchantOCRDistinguishVo initMerchantOCRDistinguish(Short type, OcrUtil.OcrReturn ocrReturn) throws JYFException {
        MerchantOCRDistinguishVo merchantOCRDistinguish = new MerchantOCRDistinguishVo();
        merchantOCRDistinguish.setDistinguishType(type);
        switch (type) {
            case 0:
                // 营业执照
                merchantOCRDistinguish.setMerchantName(ocrReturn.getData().getStr("nameCh"));
                merchantOCRDistinguish.setRegistrationCode(ocrReturn.getData().getStr("id"));
                merchantOCRDistinguish.setTermValidity(ocrReturn.getData().getStr("validDateStart") + "-" + ocrReturn.getData().getStr("validDateEnd"));
                break;
            case 1:
                // 身份证
                if (StringUtils.isNotBlank(ocrReturn.getData().getStr("name"))) {
                    merchantOCRDistinguish.setIdCardName(ocrReturn.getData().getStr("name"));
                    merchantOCRDistinguish.setIdCardCode(ocrReturn.getData().getStr("number"));
                } else {
                    if (ocrReturn.getData().getStr("timelimit").contains("长期")){
                        merchantOCRDistinguish.setIdCardSecular(1);
                    } else {
                        merchantOCRDistinguish.setIdCardSecular(0);
                    }
                    merchantOCRDistinguish.setIdCardValidity(ocrReturn.getData().getStr("timelimit"));
                }
                break;
            case 2:
                // 银行卡
                merchantOCRDistinguish.setBankAccountCode(ocrReturn.getData().getStr("bankNo"));
                merchantOCRDistinguish.setBankInfo(ocrReturn.getData().getStr("bankName"));
                // 获取银行编码信息
                BankQuery query = new BankQuery();
                query.setBankName(merchantOCRDistinguish.getBankInfo());
                SHttpResponse<List<BankVo>> banks = selectBank(query);
                if (!banks.isOk() || (banks.isOk() && CollectionUtil.isEmpty(banks.getData()))) {
                    log.error("获取银行编码信息异常");
                    throw new JYFException("获取银行编码信息异常,请联系管理员");
                }
                merchantOCRDistinguish.setBankInfoCode(banks.getData().get(0).getBankCode());
                break;
            default:
                // 前置代码已处理此情况，故此情况不会出现
                return null;
        }
        return merchantOCRDistinguish;
    }

    /**
     * 生成随机验证码
     * @param numberFlag 是否纯数字
     * @param length 验证码长度
     * @return
     */
    public static String createRandom(boolean numberFlag, int length) {
        String retStr = null;
        String strTable = numberFlag ? "1234567890" : "1234567890abcdefghijkmnpqrstuvwxyzABCDEFGHIJKMNPQRSTUVWXYZ";
        int len = strTable.length();
        boolean bDone = true;
        do {
            retStr = "";
            int count = 0;
            for (int i = 0; i < length; i++) {
                double dblR = Math.random() * len;
                int intR = (int) Math.floor(dblR);
                char c = strTable.charAt(intR);
                if (('0' <= c) && (c <= '9')) {
                    count++;
                }
                retStr += strTable.charAt(intR);
            }
            if (count >= 2) {
                bDone = false;
            }
        } while (bDone);
        return retStr;
    }
}
